package edu.calpoly;
/**
 * Hoping this works...
 *
 * @author Hoang Tran
 */
 
import java.util.*; 

public class GoBot{
   private Board board;
   private Board Ko;
   private String color;
   private String opponent;
   private Random generator; // Just for our simple agent that plays random moves

   private int whiteDepth;
   private int blackDepth;

   private boolean randomOn;
   private boolean randWhite;
   private boolean randBlack;

   public static void main(String args[]){
      Board b = new Board();
      GoBot cykill = new GoBot(b);
      
      b.placeStone("b", "A2");
      b.placeStone("w", "C4");
      Decision dec = new Decision("b", 1, 0, b);
      System.out.println(dec.score());
      System.out.println(dec.score());
      
      //Single Test
      System.out.println(b.toString());
      b.placeStone("b",4,4);
      System.out.println(b.toString());
      b.placeStone("b",5,4);
      System.out.println(b.toString());
      b.placeStone("w",6,4);
      System.out.println(b.toString());
      b.placeStone("w",3,4);
      System.out.println(b.toString());
      b.placeStone("w",4,3);
      System.out.println(b.toString());
      b.placeStone("w",5,3);
      System.out.println(b.toString());
      b.placeStone("w",4,5);
      System.out.println(b.toString());
      b.placeStone("w",5,5);
      System.out.println(b.toString());
      
      //Group test
      System.out.println(b.toString());
      b.placeStone("b",1,1);
      System.out.println(b.toString());
      b.placeStone("b",1,2);
      System.out.println(b.toString());
      b.placeStone("b",1,3);
      System.out.println(b.toString());
      
      b.placeStone("w",2,1);
      System.out.println(b.toString());
      b.placeStone("w",2,2);
      System.out.println(b.toString());
      b.placeStone("w",2,3);
      System.out.println(b.toString());
      b.placeStone("w",1,4);
      System.out.println(b.toString());
      
      // Self Capture Bug
      // A B C D E F G H J
      // 0 1 2 3 4 5 6 7 8
      b.clearBoard();
      int i = 100;

      while(i != 0){
    	  cykill.genmove("w");
    	  cykill.genmove("b");
    	  i--;
      }

      System.out.println(b.toString());
   
      
      for(Group temp: b.getGroups()){
         if (temp.contains(b.getStone(4,4))){
            System.out.println(temp.getSides());
         }
      }
      System.out.println(b.getStone(4,4).getFreeSides());
   }

   /** A more general constructor for GoBot class. No color specified.
    *
    *@param board The instance of the current game board.
    */
   public GoBot(Board board){
      this.board = board;
      this.Ko = board.clone();
      generator = new Random();

       

      whiteDepth = blackDepth = 2;
      randomOn = false;
   }

   /**
    * Constructor for GoBot class (our agent).
    *
    *@param board The instance of the current game board.
    *@param color The color that this bot plays as.
    */
   public GoBot(Board board, String color){
      this.board = board;
      this.Ko = board.clone();
      this.color = color;
      generator = new Random(); 
      whiteDepth = blackDepth = 2;
      randomOn = false; 
   }
   

   public void setRandom(String color) {
      if (color.compareToIgnoreCase("w") == 0){
         randWhite = !randWhite;
      }
      else if (color.compareToIgnoreCase("b") == 0){
         randBlack = !randBlack;
      }
   }

   public void setDepth(String color, int treeDepth) {
      if (color.compareToIgnoreCase("w") == 0){
         whiteDepth = treeDepth;
      }
      else if (color.compareToIgnoreCase("b") == 0){
         blackDepth = treeDepth;
      }
      
   }

   /**
    * Generates a move.
    *
    *@return The string representation of the intersection to play. Ex: C4
    */
   public String genmove(){
	   int x, y;
      
      do{
         x = generator.nextInt(board.getBrdsize()) + 1;
         y = generator.nextInt(board.getBrdsize()) + 1;
      } while (!board.isEmpty(x,y));
      
      board.placeStone(color,x,y); // Could possibly be done in Game. just did it here for now.
      
      x += 'A';
      y++;
      
      if(x >= 'I') x++;
      
      return (char)x + "" + y;
   }

   /**
    * 
    * Create max tree
    * @param brd 
    * @param depth how far in we wish to go.
	* @param Ko 
	* @return Decision
    */
   private Decision max(Board brd, int depth, Board Ko) {
	   int max = new Decision(color, brd).calculate_score(color);
	   ArrayList<Decision> bestDec = new ArrayList<Decision>();
	   Decision choice =  null;
	   
	   if(depth == 1){

               //if the board is empty
               if(brd.emptyVertices().size() == brd.getBrdsize()*brd.getBrdsize()){
		      max = 0;
	         } 
		   for(Stone curr : brd.emptyVertices()){
			   Decision temp = new Decision (color, curr.getX(), curr.getY(), brd);
			   // Don't Break the Ko Rule!
			   if(temp.getBoard().equals(Ko)){
	  				  continue;
		         }
		         if (!temp.isValid()){
		            continue;
		         }
			   temp.calculate_score(color);
			   if(temp.score() > max){
				   choice = temp;
				   max = temp.score();
                           bestDec.clear();
                           bestDec.add(temp);
			   }
                     else if(temp.score() == max) {
                           bestDec.add(temp);
                     }
		   }
		   
	   } else{
               Decision d = min(brd, depth-1, Ko);

               if(d != null) {
                   max = min(brd, depth-1, Ko).score();
               }

		   int tScore;
		   for(Stone curr : brd.emptyVertices()){
			   Decision temp = new Decision (color, curr.getX(), curr.getY(), brd);
			   // Don't Break the Ko Rule!
			   if(temp.getBoard().equals(Ko)){
	  				  continue;
		   	   }
                     if (!temp.isValid()){
                         continue;
                     }
			   Decision tmin = min(temp.getBoard(), depth -1, brd);
			   if(tmin == null){
				   tScore = temp.calculate_score(color);
			   } else {
				   tScore = tmin.score();
			   }
			   if(tScore > max){
				   choice = temp;
				   max = tScore;
				   temp.setScore(tScore);
                           bestDec.clear();
                           bestDec.add(temp);
			   }
                     else if(tScore == max) {
                          bestDec.add(temp);

                     }
		   }
	   }
	   if(bestDec.size() == 1) {
            return bestDec.get(0);
         }
         else if (bestDec.size() == 0) {
            return null;
         }
         else {
            return bestDec.get(generator.nextInt(bestDec.size()));
         }
	   //return choice;
   }

   private Decision min(Board brd, int depth, Board Ko) {
	   int min = new Decision(color, brd).calculate_score(color);
	   ArrayList<Decision> bestDec = new ArrayList<Decision>();
	   Decision choice =  null;
         
	   
	   if(depth == 1){
               //if the board is empty
               if(brd.emptyVertices().size() == brd.getBrdsize()*brd.getBrdsize()){
		      min = 0;
	         }

		   for(Stone curr : brd.emptyVertices()){
			   // Decision has to be made on opposing color
			   Decision temp = new Decision (opponent, curr.getX(), curr.getY(), brd);
			   // Don't Break the Ko Rule!
			   if(temp.getBoard().equals(Ko)){
	  				  continue;
		   	   }
                     if (!temp.isValid()){
                        continue;
                     }
			   temp.calculate_score(color);
			   if(temp.score() < min){
				   choice = temp;
				   min = temp.score();
                           bestDec.clear();
                           bestDec.add(temp);
			   }
                     else if(temp.score() == min) {
                           bestDec.add(temp);
                     }
		   }
	   } else{
               //set the min to a pass
               Decision d = max(brd, depth-1,Ko);
               if(d != null){
                   min = d.score();
               }
	         
		   int tScore;
		   for(Stone curr : brd.emptyVertices()){
			   // Decision has to be made on opposing color
			   Decision temp = new Decision (opponent, curr.getX(), curr.getY(), brd);
			   // Don't Break the Ko Rule!
			   if(temp.getBoard().equals(Ko)){
	  				  continue;
		   	   }
                     if (!temp.isValid()){
                         continue;
                     }
			   Decision tmax = max(temp.getBoard(), depth -1, brd);
			   if(tmax == null){
				   tScore = temp.calculate_score(color);
			   } else {
				   tScore = tmax.score();
			   }
			   if(tScore < min){
				   choice = temp;
				   min= tScore;
				   temp.setScore(tScore);
                           bestDec.clear();
                           bestDec.add(temp);
			   }
                     else if(tScore == min) {
                           bestDec.add(temp);
                           temp.setScore(tScore);
                     }
		   }
	   }
         if(bestDec.size() == 1) {
            return bestDec.get(0);
         }
         else if (bestDec.size() == 0) {
            return null;
         }
         else {
            return bestDec.get(generator.nextInt(bestDec.size()));
         }

   }
   
   /*
    * Generates a move for the specified color.
    *
    *@param The color to generate a move for. Ex: "b".
    *
    *@return The string representation of the intersection to play. Ex: C4
    */
   public String genmove(String color){
      int x, y, max;
      this.color = color;
      this.opponent = (color.compareToIgnoreCase("w") == 0 ? "b" : "w");
      
      if(color.compareToIgnoreCase("w") == 0 && randWhite || color.compareToIgnoreCase("b") == 0 && randBlack) {
          int caps, gens = 0;
          ArrayList<Stone> moves = new ArrayList<Stone>(board.emptyVertices());
          Board temp;
          
          do {
            temp = board.clone();
            Stone dec = moves.get(generator.nextInt(moves.size()));
	         x = dec.getX();
	         y = dec.getY();
            caps = temp.placeStone(color,x,y);
            gens++;
          } while((temp.equals(Ko) || caps < 0) && gens < 20);
          
          if (gens >= 20){
              return "PASS";
          }
      }
      else {
    	  // Ask the maximin method for the best move to make if any.
    	  Decision dec = max(board, (color.compareToIgnoreCase("w") == 0 ? whiteDepth : blackDepth), Ko);

	      if(dec == null) {
	         return "PASS";
	      }
	      x = dec.x();
	      y = dec.y();
      }

      this.Ko = board.clone();
      board.placeStone(color,x,y); // Could possibly be done in Game. just did it here for now.
      
      x += 'A';
      if(x >= 'I') x++;
      y++;
      /*for(Group temp : board.getGroups())
    	  System.err.println(temp);*/
      return (char)x + "" + y;
   }
   
   public void setKo(Board ko){
	   Ko = ko;
   }
   /**
    * Just a stub in case we want to later implement a genmove specifically for white.
    */
   public String genmoveWhite(){
      return "";
   }
   
   /**
    * Ditto for black.
    */
   public String genmoveBlack(){
      return "";
   }
}